#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/lexical_cast.hpp>
#include <cmath>

#include "include/SceneCtrl.h"
#include "include/Character.h"
#include "include/Utils.h"

using namespace boost;
using namespace boost::property_tree;

void SceneCtrl::CharacterOnline(GlobalGameObj *p_game_obj)
{
	int character_id = p_game_obj->p_data_head->character_id;

	Character::BasicInfo basic_info;
	Character::GetBasicInfo(p_game_obj, character_id, &basic_info);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	// todo need to fix scene id
	cout << "scene_id: " << basic_info.scene_id << endl;
	if(basic_info.scene_id == "0")
	{
	    basic_info.scene_id = "1000";
	}

	Scene::SceneInfo scene_info;
	Scene::GetSceneInfo(p_game_obj, basic_info.scene_id, &scene_info);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	Scene::SceneObj scene_obj;
	if(Scene::GetEntryPos(scene_info.entry_str, scene_obj.x, scene_obj.y))
	{
		p_game_obj->error_code = Common::ModelError;
		p_game_obj->error_str = "Scene get entrance pos error";

		return;
	}

	scene_obj.obj_type = Scene::SceneObjCharacter;
	scene_obj.obj_id = character_id;
	scene_obj.scene_id = scene_info.scene_id;
	scene_obj.scene_type = scene_info.scene_type;
	scene_obj.speed = basic_info.speed;
	scene_obj.obj_res_id = basic_info.res_id;
	scene_obj.is_moving = 0;
	scene_obj.last_time = "0";
	scene_obj.start_x = 0;
	scene_obj.start_y = 0;
	scene_obj.end_x = 0;
	scene_obj.end_y = 0;
	scene_obj.arrive_time = "0";
	scene_obj.move_tolerance = 0;

	Scene::AddSceneObj(p_game_obj, &scene_obj);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	// change scene player info
	basic_info.online = 1;
	basic_info.last_online_time = Utils::Seconds2TimeStr(0);
	Character::UpdateBasicInfo(p_game_obj, &basic_info);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

    vector<int> player_list;
	vector<int> character_list;

	Scene::GetScenePlayer(p_game_obj, basic_info.scene_id, player_list, character_list);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	Scene::UpdateScenePlayer(p_game_obj, basic_info.scene_id);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	// for character self
	ptree scene_tree;
	Scene::GetSceneInfoTree(p_game_obj, scene_info.scene_id, scene_tree);
	scene_tree.put("evt", "Scene_Enter");
	scene_tree.put("code", 0);

	ptree character_tree;
	ptree scene_obj_tree;
	Scene::SceneObj other_scene_obj;

	other_scene_obj.scene_id = basic_info.scene_id;
	other_scene_obj.obj_type = Scene::SceneObjCharacter;

    cout << __LINE__ << " in " << __FUNCTION__ << endl;
    cout << "character_list.size: " << character_list.size() << endl;
    cout << "player_list.size: " << player_list.size() << endl;

	for(unsigned int i = 0; i < character_list.size(); i++)
	{
		other_scene_obj.obj_id = character_list[i];

        cout << "other_scene_obj.obj_id: " << other_scene_obj.obj_id << endl;

		Scene::GetSceneObjTree(p_game_obj, &other_scene_obj, scene_obj_tree);
		if(Common::NoError != p_game_obj->error_code)
		{
		    cout << "get scene obj tree error for obj id: " << other_scene_obj.obj_id << endl;
		    p_game_obj->error_code = Common::NoError;

			continue;
		}

		cout << __LINE__ << " in " << __FUNCTION__ << endl;

		character_tree.push_back(make_pair("", scene_obj_tree));
	}

    cout << __LINE__ << " in " << __FUNCTION__ << endl;

	ptree add_character_tree;
	add_character_tree.put("evt", "Scene_Add");
	add_character_tree.put("sceneId", scene_info.scene_id);
	add_character_tree.put_child("character", character_tree);
	add_character_tree.put("code", 0);

	p_game_obj->out_tree.push_back(make_pair("", scene_tree));
	p_game_obj->out_tree.push_back(make_pair("", add_character_tree));

	p_game_obj->out_format_tree.put("dataType", Common::NormalData);

    cout << __LINE__ << " in " << __FUNCTION__ << endl;

	// for other characters in same scene

	if(0 == player_list.size())
	{
	    return;
	}

	add_character_tree.clear();
	character_tree.clear();

	Scene::GetSceneObjTree(p_game_obj, &scene_obj, scene_obj_tree);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

    cout << __LINE__ << " in " << __FUNCTION__ << endl;

	character_tree.push_back(make_pair("", scene_obj_tree));
	add_character_tree.put("evt", "Scene_Add");
	add_character_tree.put("sceneId", scene_obj.scene_id);
	add_character_tree.put_child("character", character_tree);
	add_character_tree.put("code", 0);

	p_game_obj->out_tree2.push_back(make_pair("", add_character_tree));

    p_game_obj->out_format_tree2.put("dataType", Common::MulticastData);
    Utils::CreateMultiPlayerTree(player_list, p_game_obj->out_format_tree2);

    cout << __LINE__ << " in " << __FUNCTION__ << endl;
}

void SceneCtrl::MoveScene(GlobalGameObj *p_game_obj)
{
    string scene_id;
    string dest_scene_id;
    int exit_id;
    try
    {
        scene_id = p_game_obj->in_tree.get<string>("sceneId");
        exit_id = p_game_obj->in_tree.get<int>("exitId");
        dest_scene_id = p_game_obj->in_tree.get<string>("destSceneId");
    }
    catch(boost::exception &ex)
    {
        p_game_obj->error_code = Common::JsonTreeError;
		p_game_obj->error_str = "in move";
		return;
    }

    if(!Scene::CanMove(scene_id, dest_scene_id))
    {
        p_game_obj->out_tree.put("evt", "Character_MoveScene");
        p_game_obj->out_tree.put("code", ErrorCode::Scene_CanNotMoveScene);
        p_game_obj->out_tree.put("errMsg", "can not move scene from " + scene_id +
            " to " + dest_scene_id);
        return;
    }

    Scene::SceneObj scene_obj;
    scene_obj.scene_id = scene_id;
    scene_obj.obj_id = p_game_obj->character_id;
    scene_obj.obj_type = Scene::SceneObjCharacter;

    Scene::GetSceneObj(p_game_obj, &scene_obj);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    Scene::SceneInfo scene_info;
    Scene::GetSceneInfo(p_game_obj, scene_id, &scene_info);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    vector<Scene::ExitInfo *> exit_info_list;
    Scene::GetExitInfo(scene_info.exit_str, exit_info_list);
    for(unsigned int i = 0; i < exit_info_list.size(); i++)
    {
        if(exit_info_list[i]->exit_id == exit_id)
        {
            if(abs(exit_info_list[i]->x - scene_obj.x) > 10 ||
               abs(exit_info_list[i]->y - scene_obj.y) > 10)
            {
               p_game_obj->error_code = Common::LogicError;
               p_game_obj->error_str = "player is too far from exit pos in move scene";
            }
        }

        delete exit_info_list[i];
        exit_info_list[i] = NULL;
    }
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    Scene::SceneInfo dest_scene_info;
    Scene::GetSceneInfo(p_game_obj, dest_scene_id, &dest_scene_info);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    if(Scene::GetEntryPos(dest_scene_info.entry_str, scene_obj.x, scene_obj.y))
    {
        p_game_obj->error_code = Common::ModelError;
        p_game_obj->error_str = "get entry pos error in move scene";
        return;
    }

    scene_obj.scene_id = dest_scene_id;
    Scene::AddSceneObj(p_game_obj, &scene_obj);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    vector<int> character_list;
    vector<int> player_list;

    Scene::GetScenePlayer(p_game_obj, dest_scene_id, player_list, character_list);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    Scene::UpdateScenePlayer(p_game_obj, dest_scene_id);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	// for character self
	ptree scene_tree;
	Scene::GetSceneInfoTree(p_game_obj, dest_scene_id, scene_tree);
	scene_tree.put("evt", "Scene_Enter");
	scene_tree.put("code", 0);

	ptree character_tree;
	ptree scene_obj_tree;
	Scene::SceneObj other_scene_obj;

	other_scene_obj.scene_id = dest_scene_id;
	other_scene_obj.obj_type = Scene::SceneObjCharacter;

	for(unsigned int i = 0; i < character_list.size(); i++)
	{
		other_scene_obj.obj_id = character_list[i];

		Scene::GetSceneObjTree(p_game_obj, &other_scene_obj, scene_obj_tree);
		if(Common::NoError != p_game_obj->error_code)
		{
			continue;
		}

		character_tree.push_back(make_pair("", scene_obj_tree));
	}

	ptree add_character_tree;
	add_character_tree.put("evt", "Scene_Add");
	add_character_tree.put("sceneId", dest_scene_id);
	add_character_tree.put_child("character", character_tree);
	add_character_tree.put("code", 0);

	p_game_obj->out_tree.push_back(make_pair("", scene_tree));
	p_game_obj->out_tree.push_back(make_pair("", add_character_tree));

	p_game_obj->out_format_tree.put("dataType", Common::NormalData);

	// for other characters in same scene
	add_character_tree.clear();
	character_tree.clear();

	Scene::GetSceneObjTree(p_game_obj, &scene_obj, scene_obj_tree);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

	character_tree.push_back(make_pair("", scene_obj_tree));
	add_character_tree.put("evt", "Scene_Add");
	add_character_tree.put("sceneId", dest_scene_id);
	add_character_tree.put_child("character", character_tree);
	add_character_tree.put("code", 0);

    p_game_obj->out_tree2.push_back(make_pair("", add_character_tree));

    p_game_obj->out_format_tree2.put("dataType", Common::MulticastData);
    Utils::CreateMultiPlayerTree(player_list, p_game_obj->out_format_tree2);
}

void SceneCtrl::StartMove(GlobalGameObj *p_game_obj)
{
	string scene_id;
	int start_x;
	int start_y;
	int end_x;
	int end_y;
	try
	{
	    scene_id = p_game_obj->in_tree.get<string>("sceneId");
		start_x = p_game_obj->in_tree.get<int>("x");
		start_y = p_game_obj->in_tree.get<int>("y");
		end_x = p_game_obj->in_tree.get<int>("endX");
		end_y = p_game_obj->in_tree.get<int>("endY");
	}
	catch(boost::exception &ex)
	{
		p_game_obj->error_code = Common::JsonTreeError;
		p_game_obj->error_str = "in start move scene";
		return;
	}

    Scene::SceneObj scene_obj;

    scene_obj.scene_id = scene_id;
    scene_obj.obj_type = Scene::SceneObjCharacter;
    scene_obj.obj_id = p_game_obj->character_id;

    Scene::GetSceneObj(p_game_obj, &scene_obj);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    if(abs(scene_obj.x - start_x) > Scene::tolerance_distence ||
       abs(scene_obj.y - start_y) > Scene::tolerance_distence)
    {
        p_game_obj->error_code = Common::LogicError;
        p_game_obj->error_str = "start pos is not right in start move";
#ifdef _DEBUG_LEVEL_2_
        cout << "in start move, character id: " << scene_obj.obj_id << ", x in param: " <<
            start_x << ", y in param: " << start_y << ", x in server: " << scene_obj.x <<
            ", y in server: " << scene_obj.y << endl;
#endif
       return;
    }

    Scene::SceneInfo scene_info;
    Scene::GetSceneInfo(p_game_obj, scene_id, &scene_info);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    if(start_x >= scene_info.max_x || start_y >= scene_info.max_y ||
       end_x >= scene_info.max_x || end_y >= scene_info.max_y)
    {
        p_game_obj->error_code = Common::LogicError;
        p_game_obj->error_str = "moving pos is invalid in start move";
#ifdef _DEBUG_LEVEL_2_
        cout << "in start move, character id: " << scene_obj.obj_id << ", start x in param" <<
            start_x << ", start y in param: " << start_y << ", end x in param: " <<
            end_x << ", end y in param: " << end_y << ", max x in scene: " <<
            scene_info.max_x << ", max y in scene: " << scene_info.max_y << endl;
#endif
        return;
    }

	int distence = Utils::Distence(start_x, start_y, end_x, end_y);
	if(0 == distence)
	{
	    p_game_obj->error_code = Common::LogicError;
	    p_game_obj->error_str = "move distence is 0 in start move";
#ifdef _DEBUG_LEVEL_2_
        cout << "in start move, character id: " << scene_obj.obj_id << ", start x in param" <<
            start_x << ", start y in param: " << start_y << ", end x in param: " <<
            end_x << ", end y in param: " << end_y << endl;
#endif
	    return;
	}

    int milli_sec = distence / scene_obj.speed;
    string arrive_time_str = Utils::MilliSec2TimeStr(milli_sec);

    scene_obj.start_x = start_x;
    scene_obj.start_y = start_y;
    scene_obj.end_x = end_x;
    scene_obj.end_y = end_y;
    scene_obj.is_moving = 1;
    scene_obj.last_time = Utils::MilliSec2TimeStr(0);
    scene_obj.x = start_x;
    scene_obj.y = start_y;
    scene_obj.arrive_time = arrive_time_str;
    scene_obj.move_tolerance = 0;

    Scene::AddSceneObj(p_game_obj, &scene_obj);
    ptree character_tree;
    Scene::GetSceneObjTree(p_game_obj, &scene_obj, character_tree);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

    ptree start_move_tree;

    start_move_tree.put("evt", "Scene_StartMove");
    start_move_tree.put("sceneId", scene_id);
    start_move_tree.put_child("character", character_tree);
    start_move_tree.put("code", 0);

    p_game_obj->out_tree.push_back(make_pair("", start_move_tree));

    vector<int> player_list;
    vector<int> character_list;
    Scene::GetScenePlayer(p_game_obj, scene_id, player_list, character_list);

    p_game_obj->out_format_tree.put("dataType", Common::NormalData);
    Utils::CreateMultiPlayerTree(player_list, p_game_obj->out_format_tree);
}

//void SceneCtrl::Move(GlobalGameObj *p_game_obj)
//{
//	string scene_id;
//	int x;
//	int y;
//
//	try
//	{
//		scene_id = p_game_obj->in_tree.get<string>("sceneId");
//		x = p_game_obj->in_tree.get<int>("x");
//		y = p_game_obj->in_tree.get<int>("y");
//	}
//	catch(boost::exception &ex)
//	{
//		p_game_obj->error_code = Common::JsonTreeError;
//		p_game_obj->error_str = "in move";
//		return;
//	}
//
//	Scene::SceneObj scene_obj;
//
//	scene_obj.scene_id = scene_id;
//    scene_obj.obj_type = Scene::SceneObjCharacter;
//    scene_obj.obj_id = p_game_obj->character_id;
//
//	Scene::GetSceneObj(p_game_obj, &scene_obj);
//    if(Common::NoError != p_game_obj->error_code)
//    {
//        return;
//    }
//
//	if(1 != scene_obj.is_moving)
//	{
//		StopMove(p_game_obj, &scene_obj, p_game_obj->out_tree);
//		return;
//	}
//
//	if(!(((x >= scene_obj.x && x <= scene_obj.end_x) ||
//		(x <= scene_obj.x && x >= scene_obj.end_x)) &&
//		((y >= scene_obj.y && y <= scene_obj.end_y) ||
//		(y <= scene_obj.y && y >= scene_obj.end_y))))
//	{
//		StopMove(p_game_obj, &scene_obj, p_game_obj->out_tree);
//		return;
//	}
//
//	int duration = 0 - Utils::TimeStr2MilliSec(scene_obj.last_time);
//	if(duration <= 0)
//	{
//		p_game_obj->error_code = Common::LogicError;
//		p_game_obj->error_str = "duration <= 0 in move";
//		return;
//	}
//
//	int stretch = duration * scene_obj.speed;
//	int distence = Utils::Distence(scene_obj.x, scene_obj.y, x, y);
//
//	scene_obj.move_tolerance += distence - stretch;
//	if(scene_obj.move_tolerance > Scene::total_tolerance ||
//		scene_obj.move_tolerance < -Scene::total_tolerance)
//	{
//		StopMove(p_game_obj, &scene_obj, p_game_obj->out_tree);
//		return;
//	}
//
//	scene_obj.last_time = Utils::MilliSec2TimeStr(0);
//	scene_obj.x = x;
//	scene_obj.y = y;
//
//	p_game_obj->p_package_head->append_data = Common::EmptyData;
//}

void SceneCtrl::StopMove(GlobalGameObj *p_game_obj, Scene::SceneObj *p_scene_obj,
    ptree &stop_tree, ptree &format_tree)
{
	ptree stop_move_tree;
	p_scene_obj->is_moving = 0;

	Scene::AddSceneObj(p_game_obj, p_scene_obj);
	if(Common::NoError != p_game_obj->error_code)
	{
		return;
	}

    ptree character_tree;
    Scene::GetSceneObjTree(p_game_obj, p_scene_obj, character_tree);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

	stop_move_tree.put("evt", "Scene_StopMove");
	stop_move_tree.put("sceneId", p_scene_obj->scene_id);
	stop_move_tree.put_child("character", character_tree);
	stop_move_tree.put("code", 0);

	stop_tree.push_back(make_pair("", stop_move_tree));

	vector<int> player_list;
	vector<int> character_list;
	Scene::GetScenePlayer(p_game_obj, p_scene_obj->scene_id, player_list, character_list);

    format_tree.put("dataType", Common::MulticastData);
    Utils::CreateMultiPlayerTree(player_list, format_tree);
}

void SceneCtrl::StopMove(GlobalGameObj *p_game_obj)
{
	string scene_id;
	int x;
	int y;

	try
	{
		scene_id = p_game_obj->in_tree.get<string>("sceneId");
		x = p_game_obj->in_tree.get<int>("x");
		y = p_game_obj->in_tree.get<int>("y");
	}
	catch(boost::exception &ex)
	{
		p_game_obj->error_code = Common::JsonTreeError;
		p_game_obj->error_str = "in stop move";
		return;
	}

	Scene::SceneObj scene_obj;

	scene_obj.scene_id = scene_id;
    scene_obj.obj_type = Scene::SceneObjCharacter;
    scene_obj.obj_id = p_game_obj->character_id;

	Scene::GetSceneObj(p_game_obj, &scene_obj);
    if(Common::NoError != p_game_obj->error_code)
    {
        return;
    }

	if(1 != scene_obj.is_moving)
	{
		StopMove(p_game_obj, &scene_obj, p_game_obj->out_tree, p_game_obj->out_format_tree);
		return;
	}

	if(!(((x >= scene_obj.x && x <= scene_obj.end_x) ||
		(x <= scene_obj.x && x >= scene_obj.end_x)) &&
		((y >= scene_obj.y && y <= scene_obj.end_y) ||
		(y <= scene_obj.y && y >= scene_obj.end_y))))
	{
		StopMove(p_game_obj, &scene_obj, p_game_obj->out_tree, p_game_obj->out_format_tree);
		return;
	}

	int duration = 0 - Utils::TimeStr2MilliSec(scene_obj.last_time);
	if(duration <= 0)
	{
		p_game_obj->error_code = Common::LogicError;
		p_game_obj->error_str = "duration <= 0 in move";
		return;
	}

	int stretch = duration * scene_obj.speed;
	int distence = Utils::Distence(scene_obj.x, scene_obj.y, x, y);

	scene_obj.move_tolerance += distence - stretch;
	if(scene_obj.move_tolerance > Scene::total_tolerance ||
		scene_obj.move_tolerance < -Scene::total_tolerance)
	{
		StopMove(p_game_obj, &scene_obj, p_game_obj->out_tree, p_game_obj->out_format_tree);
		return;
	}

	scene_obj.last_time = Utils::MilliSec2TimeStr(0);
	scene_obj.x = x;
	scene_obj.y = y;

	StopMove(p_game_obj, &scene_obj, p_game_obj->out_tree, p_game_obj->out_format_tree);
}
